package com.nulldev.util.web.nTube.pages;

import java.net.SocketTimeoutException;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import com.nulldev.util.VariableAPI.RandomUtil;
import com.nulldev.util.languages.json.uniapi.UniJsonValue;
import com.nulldev.util.logging.Logger;
import com.nulldev.util.logging.LoggerFactory;
import com.nulldev.util.web.HttpServer3.Response;
import com.nulldev.util.web.HttpServer3.pages.v3.HttpPage3;
import com.nulldev.util.web.URLUtils.URLQueryParser;
import com.nulldev.util.web.URLUtils.URLQueryParser.URLQueryParserOptions;
import com.nulldev.util.web.YouTubeExtractor.channel.ChannelCommunityExtractor;
import com.nulldev.util.web.YouTubeExtractor.channel.ChannelCommunityExtractor.CommunityChannel;
import com.nulldev.util.web.YouTubeExtractor.channel.ChannelCommunityExtractor.CommunityEntry;
import com.nulldev.util.web.YouTubeExtractor.channel.ChannelExtractor;
import com.nulldev.util.web.YouTubeExtractor.channel.ChannelExtractor.Album;
import com.nulldev.util.web.YouTubeExtractor.channel.ChannelExtractor.Channel;
import com.nulldev.util.web.YouTubeExtractor.channel.ChannelExtractor.Thumbnail;
import com.nulldev.util.web.YouTubeExtractor.channel.ChannelExtractor.Video;
import com.nulldev.util.web.YouTubeExtractor.channel.ChannelSubpageExtractor;
import com.nulldev.util.web.YouTubeExtractor.channel.ChannelSubpageExtractor.SubPageType;

public class ChannelAPIPage extends HttpPage3 {

	private static final Logger log = LoggerFactory.getLogger();

	public ChannelAPIPage() {
		super("/api/channel");
		super.setCachingMethod(CachingMethod.NO_CACHE);
		super.setUseJSONCaptchaPage(true);
	}

	private static final String JSON_MIME = "application/json; charset=utf-8";

	private static UniJsonValue __compat_makeImagesObj(final Channel c) {
		final List<Thumbnail> banners = c.getBanners();
		if (banners == null || banners.isEmpty()) {
			return UniJsonValue.emptyObject();
		}
		final UniJsonValue images = UniJsonValue.object(2);
		if (banners.size() == 1) {
			images.add("standardBanner", banners.get(0).url());
		} else {
			{
				final Optional<Thumbnail> _temp = banners.stream()
						.filter(thumb -> thumb.hasFlag() && thumb.flag().equals(ChannelExtractor.MAXIMUM_RESOLUTION_FLAG)).findFirst();
				if (_temp.isPresent()) {
					images.add("qualityBanner", _temp.get().url());
				} else {
					log.warn("[__compat_makeImagesObj] Failed to find a thumbnail with MAXIMUM_RESOLUTION_FLAG!");
				}
			}
			{
				final Optional<Thumbnail> _temp = banners.stream()
						.filter(thumb -> thumb.hasFlag() && thumb.flag().equals(ChannelExtractor.MINIMUM_RESOLUTION_FLAG)).findFirst();
				if (_temp.isPresent()) {
					images.add("standardBanner", _temp.get().url());
				} else {
					log.warn("[__compat_makeImagesObj] Failed to find a thumbnail with MINIMUM_RESOLUTION_FLAG!");
				}
			}
		}
		return images;
	}

	private static String __compat_makeAvatarObj(final Channel c) {
		final List<Thumbnail> banners = c.getAvatars();
		if (banners == null || banners.isEmpty()) {
			return null;
		}
		if (banners.size() == 1) {
			return banners.get(0).url();
		} else {
			final Optional<Thumbnail> _temp = banners.stream().filter(thumb -> thumb.hasFlag() && thumb.flag().equals(ChannelExtractor.MINIMUM_RESOLUTION_FLAG))
					.findFirst();
			if (_temp.isPresent()) {
				return _temp.get().url();
			} else {
				log.warn("[__compat_makeImagesObj] Failed to find a thumbnail with MINIMUM_RESOLUTION_FLAG!");
				return null;
			}
		}
	}

	@SuppressWarnings("resource")
	@Override
	public Response onRequest(final Request _request) {
		final String args = _request.url();
		if (args == "") {
			final UniJsonValue err = UniJsonValue.object(2);
			err.add("error", true);
			err.add("reason", "INVALID_REQUEST");
			return new Response(200, err, err).setCustomMime(JSON_MIME);
		}
		final Map<String, String> request = URLQueryParser.extract(args, URLQueryParserOptions.PREFER_LEGACY_IF_ACCEL_V1);
		final String channel = request.get("c");
		if (channel == null || channel.isEmpty()) {
			final UniJsonValue err = UniJsonValue.object(2);
			err.add("error", true);
			err.add("reason", "NO_CHANNEL_PROVIDED");
			return new Response(200, err, err).setCustomMime(JSON_MIME);
		}
		final boolean bitchute = request.containsKey("bc");
		final boolean fromSub = request.containsKey("sub");
		final UniJsonValue jso = UniJsonValue.object();
		String backend = "channel";
		try {
			if (request.containsKey("backend"))
				backend = request.get("backend");
		} catch (Exception e) {
		}
		String cont = null;
		try {
			if (request.containsKey("cont"))
				cont = request.get("cont");
		} catch (Exception e) {
		}
		String ctp = null;
		try {
			if (request.containsKey("ctp"))
				ctp = request.get("ctp");
		} catch (Exception e) {
		}
		switch (backend) {
			case "channel-sub":
			case "channel": {
				if (bitchute) {
					jso.add("channelName", "NTubeServer.ext.bitchute.lang.unsupported");
					jso.add("familyFriendly", true);
					jso.add("paidContent", false);
					jso.add("error", true);
					final UniJsonValue arr = UniJsonValue.array();
					jso.add("videos", arr);
				} else {
					if (fromSub) {
						try {
							Thread.sleep(RandomUtil.randLong(400, 1200));
						} catch (InterruptedException e) {
							Thread.interrupted();
						}
					}
					try {
						final Channel videos = ChannelExtractor.extract(channel, cont, ctp);
						if (videos == null) {
							jso.add("error", true);
							return new Response(200, createStream(jso.toString())).setCustomMime("application/json");
						}
						if (videos.hasContinuation()) {
							jso.add("continuation", videos.getContinuation());
						}
						if (videos.hasContClickTracking()) {
							jso.add("contClickTracking", videos.getContClickTracking());
						}
						if (videos.isMusicChannel()) {
							final UniJsonValue meta = UniJsonValue.object();
							meta.add("channelName", videos.getChannelName());
							meta.add("isMusicChannel", true);
							meta.add("images", UniJsonValue.object());
							meta.add("familyFriendly", true);
							meta.add("paidContent", false);
							meta.add("description", "No description avaliable.");
							meta.add("channelID", videos.getChannelID());
							jso.add("metadata", meta);
							final UniJsonValue albums = UniJsonValue.array(videos.getAlbums().size());
							for (final Album am : videos.getAlbums()) {
								final UniJsonValue o = UniJsonValue.object();
								o.add("title", am.getTitle());
								o.add("thumbnail", am.getThumbnail());
								o.add("url", am.getURL());
								o.add("count", am.trackCount());
								albums.add(o);
							}
							jso.add("albums", albums);
						} else {
							final UniJsonValue meta = UniJsonValue.object();
							meta.add("channelName", videos.getChannelName());
							meta.add("images", __compat_makeImagesObj(videos));
							meta.add("familyFriendly", videos.isFamilyFriendly());
							meta.add("paidContent", videos.hasPaidContent());
							meta.add("description", videos.getDescription());
							try {
								meta.add("lastVid", videos.getVideos().get(0).getID());
							} catch (Exception e) {
								log.error("channel_api -> Failed to get the last video...");
							}
							meta.add("channelID", videos.getChannelID());
							meta.add("subscribers", videos.getSubscriberCount());
							meta.add("avatarURL", __compat_makeAvatarObj(videos));
							jso.add("metadata", meta);
							final UniJsonValue arr = UniJsonValue.array(videos.getVideos().size());
							for (final Video v : videos.getVideos()) {
								final UniJsonValue obj = UniJsonValue.object();
								obj.add("title", v.getTitle());
								obj.add("url", v.getURL());
								obj.add("thumbnail", v.getThumbURL());
								obj.add("videoID", v.getID());
								obj.add("duration", v.duration());
								if (v.hasAnimatedThumbnail())
									obj.add("ani_thumb", v.getAnimatedThumbnailURL());
								if (v.isUpcoming() && v.hasUpcomingStartTime())
									obj.add("startTime", v.getUpcomingStartTime());
								arr.add(obj);
							}
							jso.add("videos", arr);
						}
						videos.clear();
					} catch (Exception e) {
						jso.add("error", true);
						if (!(e instanceof SocketTimeoutException))
							log.error(e);
					}
				}
				break;
			}
			case "livestreams":
			case "shorts": {
				try {
					final Channel videos = ChannelSubpageExtractor.extract(channel, cont,
							(backend.equalsIgnoreCase("shorts") ? SubPageType.SHORTS : SubPageType.LIVESTREAMS));
					if (videos == null) {
						jso.add("error", true);
						return new Response(200, createStream(jso.toString())).setCustomMime("application/json");
					}
					if (videos.hasContinuation()) {
						jso.add("continuation", videos.getContinuation());
					}
					if (videos.isMusicChannel()) {
						final UniJsonValue meta = UniJsonValue.object();
						meta.add("channelName", videos.getChannelName());
						meta.add("isMusicChannel", true);
						meta.add("images", UniJsonValue.object());
						meta.add("familyFriendly", true);
						meta.add("paidContent", false);
						meta.add("description", "No description avaliable.");
						meta.add("channelID", videos.getChannelID());
						jso.add("metadata", meta);
						final UniJsonValue albums = UniJsonValue.array(videos.getAlbums().size());
						for (final Album am : videos.getAlbums()) {
							final UniJsonValue o = UniJsonValue.object();
							o.add("title", am.getTitle());
							o.add("thumbnail", am.getThumbnail());
							o.add("url", am.getURL());
							o.add("count", am.trackCount());
							albums.add(o);
						}
						jso.add("albums", albums);
					} else {
						final UniJsonValue meta = UniJsonValue.object();
						meta.add("channelName", videos.getChannelName());
						meta.add("images", __compat_makeImagesObj(videos));
						meta.add("familyFriendly", videos.isFamilyFriendly());
						meta.add("paidContent", videos.hasPaidContent());
						meta.add("description", videos.getDescription());
						try {
							meta.add("lastVid", videos.getVideos().get(0).getID());
						} catch (Exception e) {
							log.error("channel_api -> Failed to get the last video...");
						}
						meta.add("channelID", videos.getChannelID());
						meta.add("subscribers", videos.getSubscriberCount());
						meta.add("avatarURL", __compat_makeAvatarObj(videos));
						jso.add("metadata", meta);
						final UniJsonValue arr = UniJsonValue.array(videos.getVideos().size());
						for (final Video v : videos.getVideos()) {
							final UniJsonValue obj = UniJsonValue.object();
							obj.add("title", v.getTitle());
							obj.add("url", v.getURL());
							obj.add("thumbnail", v.getThumbURL());
							obj.add("videoID", v.getID());
							obj.add("duration", v.duration());
							if (v.hasAnimatedThumbnail())
								obj.add("ani_thumb", v.getAnimatedThumbnailURL());
							if (v.isUpcoming() && v.hasUpcomingStartTime())
								obj.add("startTime", v.getUpcomingStartTime());
							arr.add(obj);
						}
						jso.add("videos", arr);
					}
					videos.clear();
				} catch (Exception e) {
					jso.add("error", true);
					if (!(e instanceof SocketTimeoutException))
						log.error(e);
				}
				break;
			}
			case "community": {
				try {
					final CommunityChannel cc = ChannelCommunityExtractor.extract(channel, cont);
					jso.add("channel", channel);
					if (cc.hasContinuations())
						jso.add("continuation", cc.getContinuation());
					final UniJsonValue arr = UniJsonValue.array(cc.getEntries().size());
					for (final CommunityEntry e : cc.getEntries()) {
						final UniJsonValue entry = UniJsonValue.object();
						entry.add("author", e.getAuthor());
						entry.add("content", e.getComment());
						if (e.hasImages()) {
							final UniJsonValue images = UniJsonValue.array(e.getImages().size());
							if (!e.getImages().isEmpty()) {
								e.getImages().forEach((il) -> {
									if (il.isEmpty())
										return;
									final UniJsonValue _i = UniJsonValue.array(il.size());
									il.forEach((image) -> {
										final UniJsonValue i = UniJsonValue.object();
										i.add("url", image.getURL());
										i.add("width", image.getWidth());
										i.add("height", image.getHeight());
										i.add("idx", image.getIndex());
										_i.add(i);
									});
									images.add(_i);
								});
							}
							entry.add("images", images);
						}
						if (e.hasVideos()) {
							final UniJsonValue vids = UniJsonValue.array(e.getVideos().size());
							e.getVideos().forEach((video) -> {
								final UniJsonValue v = UniJsonValue.object();
								v.add("videoID", video.getVideoID());
								vids.add(v);
							});
							entry.add("videos", vids);
						}
						if (e.hasPolls()) {
							final UniJsonValue polls = UniJsonValue.array(e.getPolls().size());
							e.getPolls().forEach(p -> {
								final UniJsonValue poll = UniJsonValue.object();
								final UniJsonValue options = UniJsonValue.array(p.getOptions().size());
								p.getOptions().forEach(options::add);
								poll.add("options", options);
								polls.add(poll);
							});
							entry.add("polls", polls);
						}
						arr.add(entry);
					}
					jso.add("entries", arr);
				} catch (Exception e) {
					jso.add("error", true);
					log.error(e);
				}
				break;
			}
			default: {
				jso.add("error", true);
				jso.add("error_message", "Invalid backend!");
			}
		}
		return new Response(200, jso, jso).setCustomMime(JSON_MIME);
	}

}
